#ifndef __FUNCS_H__
#define __FUNCS_H__

#include "Spark/Utils/Logger.h"
#include <vector>
#include <sstream>
#include <string>
#include <assert.h>

#ifdef __GNUC__

#define LIKELY_(x) __builtin_expect(!!(x), 1)
#define UNLIKELY_(x) __builtin_expect(!!(x), 0)
#define ASSERT_(x)                                             \
    if (UNLIKELY_(!(x)))                                       \
    {                                                          \
        LOG_ERROR << "ASSERT FAIL: " << #x << "\nBackTrace:\n" \
                  << backTraceToString(100, 2, " >>> ");       \
        assert(x);                                             \
    }

#else

#define LIKELY_(x) (x)
#define UNLIKELY_(x) (x)
#define ASSERT_(x) assert(x)

#endif

/**
 * @brief Split a string with given delimiter
 * @param  s String to be splitted
 * @param  delimiter Delimiter
 * @param  acceptEmptyString Does not accept empty string by default
 * @return std::vector<std::string>
 */
inline std::vector<std::string> splitString(const std::string &s,
                                            const std::string &delimiter,
                                            bool acceptEmptyString = false)
{
    if (delimiter.empty())
        return std::vector<std::string>{};
    std::vector<std::string> v;
    size_t last = 0;
    size_t next = 0;
    while ((next = s.find(delimiter, last)) != std::string::npos)
    {
        if (next > last || acceptEmptyString)
            v.push_back(s.substr(last, next - last));
        last = next + delimiter.length();
    }
    if (s.length() > last || acceptEmptyString)
        v.push_back(s.substr(last));
    return v;
}

template <class T>
struct is_iterator
{
    static char test(...);
    // An iterator shall have following traits
    template <typename U,
              typename std::iterator_traits<U>::difference_type,
              typename std::iterator_traits<U>::pointer,
              typename std::iterator_traits<U>::reference,
              typename std::iterator_traits<U>::value_type,
              typename std::iterator_traits<U>::iterator_category>
    static long test(U &&);

    static constexpr bool value =
        std::is_same<decltype(test(std::declval<T>())), long>::value;
};

/**
 * @brief  Joining elements from a given iterator range into a string with
 * delimiter
 * @tparam Iter Using std::enable_if to make sure the given arguments comply the
 * iterator_traits
 * @param  begin Iterator to the beginn
 * @param  end Iterator to the end
 * @param  delim Delimiter
 * @return std::string
 */
template <class Iter>
typename std::enable_if<is_iterator<Iter>::value, std::string>::type
iterStrJoin(Iter begin, Iter end, const std::string &delim = "")
{
    std::stringstream ss;
    for (Iter it = begin; it != end; ++it)
    {
        if (it != begin)
        {
            ss << delim;
        }
        ss << *it;
    }
    return ss.str();
}

/**
 * @brief  Acquire current call stack
 * @param  bt Saved call stack
 * @param  size Maximum stack size
 * @param  skip Levels skipped from stack top
 */
void backTrace(std::vector<std::string> &bt, int size = 64, int skip = 1);

/**
 * @brief  Acquire string from current stack info
 * @param  size Maximum stack size
 * @param  skip Levels skipped from stack top
 * @param  prefix Prefix string before stack info string
 * @return std::string stack info
 */
std::string backTraceToString(int size = 64,
                              int skip = 2,
                              const std::string &prefix = "");

#endif  // __FUNCS_H__